home *** CD-ROM | disk | FTP | other *** search
/ CD-ROM Data 2002 May / CD Rom Data Mayıs 2002.iso / Freeware / Blitz Basic / data1.cab / Support / help / samples / maze / mazelib.bb < prev    next >
Encoding:
Text File  |  2002-04-10  |  14.8 KB  |  648 lines

  1. ; maze generator library
  2. ; (c) Graham Kennedy (Blitztastic) 2001
  3.  
  4.  
  5. ; version 1.0 - Original build
  6. ; version 1.1 - Various fixes
  7. ; version 1.2 - Replaced global array with independent type/bank
  8. ;             - Packaged it into a more standard 'library' approach
  9. ; version 1.3 - Added more 'friendly functions'
  10. ; version 1.4 - Added maze solver code (currently for perfect mazes only)
  11. ;
  12. ; Still to Do:
  13. ; More error checking, eg. save successful, load successful etc.
  14. ;
  15. ; Any comments / suggestions please email blitztastic@bigfoot.com
  16. ; This library is free to use in your software, but I'd like it if you could include
  17. ; an acknowledgement somewhere, or better still a copy.
  18. ; ---------------------------------------------------------------
  19. ; Maze generation library
  20. ; Generates a 'perfect' maze, ie a maze with only one solution between any 
  21. ; two points. A non-perfect maze, ie. one with loops can be created by setting the 
  22. ; crossover parameter to > 0 when creating the maze
  23. ;
  24. ; User Types
  25. ; Maze
  26. ;   contains the width/height of the maze, as well as the data, you should only
  27. ;   need to create a variable of this type to hold it, all access is performed by
  28. ;   functions.
  29.  
  30. ; User commands
  31. ; maze_startup(filename$,imagewidth,imageheight)
  32. ;  Initialises the maze library, call this after your graphics command but
  33. ;  before you use any other maze commands
  34. ;  parameters:
  35. ;        filename = name of a graphics file for displaying the maze
  36. ;        imagewidth  = required width for a displayed cell
  37. ;        imageheight = required height for a displayed cell
  38.  
  39. ; maze_shutdown()
  40. ;  Shuts down and cleans up the maze library, use it before the program terminates
  41.  
  42. ; maze_create(width,height,seed,crossovers)
  43. ;  This will create a new maze.
  44. ;  Parameters:
  45. ;        width         = the width of the maze to create
  46. ;         height         = the height of the maze to create
  47. ;        seed           = a seed to create the maze randomly, entering the same
  48. ;                       seed to multiple create's should create the same maze
  49. ;        crossovers  = the number of crossovers you would like to try and create
  50. ;                      in the maze, specifying 0 will create a perfect maze.
  51. ;  Return:
  52. ;        a type variable of type 'MAZE'
  53.  
  54. ; maze_display(m.maze)
  55. ;  display a maze on screen using a very basic method, modify for use in your app
  56. ;  parameters:
  57. ;        The maze variable of the maze you want to display
  58.  
  59. ; maze_getcell(m.maze,x,y)
  60. ;  gets the value of the cell in the maze at position x, y
  61. ;  Parameters :
  62. ;        m.maze  = maze to examine
  63. ;        x        = x position
  64. ;        y        = y position
  65. ;  Returns :
  66. ;        integer value at that cell, encoded as bits
  67. ;        ???1 = North open
  68. ;        ??1? = East Open
  69. ;        ?1?? = South Open
  70. ;        1??? = West open
  71. ;   eg. if 3 was returned (ie. ??11) north and east are open
  72. ;        
  73.  
  74. ; maze_setcell(m.maze,x,y,value)
  75. ;    Sets the value of the cell in the maze at position x,y
  76. ;   Parameters:
  77. ;        m.maze  = maze to examine
  78. ;        x        = x position
  79. ;        y        = y position
  80. ;          Value    integer value to store at that cell, encoded as bits
  81. ;        ???1 = North open
  82. ;        ??1? = East Open
  83. ;        ?1?? = South Open
  84. ;        1??? = West open
  85. ;   eg. if 3 was stored (ie. ??11) north and east are open
  86.  
  87. ; maze_DirOpen(m.maze,x,y,direction)
  88. ;   Indicates if a given direction is 'open' in a cell
  89. ;    Parameters:
  90. ;        m.maze    = maze to examine
  91. ;        x        = x position
  92. ;        y        = y position
  93. ;        direction= direction to look
  94. ;                        0 - north
  95. ;                        1 - east
  96. ;                        2 - south
  97. ;                        3 - west
  98. ;    Returns:
  99. ;         true if the direction is open, otherwise false
  100.  
  101. ; maze_NorthOpen(m.maze,x,y)
  102. ; maze_EastOpen(m.maze,x,y)
  103. ; maze_SouthOpen(m.maze,x,y)
  104. ; maze_WestOpen(m.maze,x,y)
  105. ;    Same functionality as maze_diropen, but in friendlier terms.
  106.  
  107. ; maze_solve(m.maze,sx,sy,fx,fy)
  108. ;     creates a path from one point of the maze to another (currently only works on perfect mazes)
  109. ;     parameters:
  110. ;        m.maze     = the maze to solve
  111. ;        sx        = the x position to start from
  112. ;        sy        = the y position to start from
  113. ;        fx        = the x position to finish at
  114. ;        fy        = the y position to finish at
  115. ;    returns:
  116. ;        a string containing the directions to move eg. "0112123" = North,East,East,South,East,South,West
  117.  
  118. ; maze_save(m.maze,filename$)
  119. ;    Saves the current maze (& route information if available) to disk
  120. ;   parameters:
  121. ;        m.maze      = maze to save
  122. ;        filename$ = name of the file to save it as
  123.  
  124. ; maze_Load(filename$)
  125. ;    Saves the current maze (& route information if available) to disk
  126. ;   parameters:
  127. ;        filename$ = name of the file to LOAD
  128. ;    returns:
  129. ;        a maze type (same as create)
  130.  
  131. .maze_endofintro
  132.  
  133. ; the main structure for a maze, use 'maze_create' to create one of these
  134. Type maze
  135.     Field width,height
  136.     Field buffer
  137.     Field crossing
  138. End Type
  139.  
  140. ; internal representation of a maze cell, don't use in your code
  141. Type maze_loc
  142.     Field x,y
  143. End Type
  144.  
  145. ; internal control structure, don't use in your code
  146. Type maze_control
  147.     Field imgname$
  148.     Field imgwidth, imgheight
  149.     Field img
  150.     Field count
  151. End Type
  152.  
  153. Function maze_getcell(m.maze,x,y)
  154.     Return PeekByte(m\buffer,y*m\width+x)
  155. End Function
  156.  
  157. Function maze_setcell(m.maze,x,y,value)
  158.     PokeByte m\buffer,y*m\width+x,value
  159. End Function
  160.  
  161. Function maze_GetDir(m.maze,x,y,dir)
  162.     Return (maze_getcell(m,x,y) And (2^dir))
  163. End Function
  164.  
  165. Function maze_SetDir(m.maze,x,y,dir,value)
  166.     c = maze_getcell(m,x,y) And (255-(2^dir))
  167.     If value = 1 Then
  168.         c = c Or (2^dir)
  169.     End If
  170.     maze_setcell(m,x,y,c)
  171. End Function
  172.  
  173. Function maze_DirOpen(m.maze,x,y,dir)
  174.     Return (maze_getDir(m,x,y,dir) = (2^dir))
  175. End Function
  176.  
  177. Function maze_NorthOpen(m.maze,x,y)
  178.     Return maze_diropen(m,x,y,0)
  179. End Function
  180.  
  181. Function maze_EastOpen(m.maze,x,y)
  182.     Return maze_diropen(m,x,y,1)
  183. End Function
  184.  
  185. Function maze_SouthOpen(m.maze,x,y)
  186.     Return maze_diropen(m,x,y,2)
  187. End Function
  188.  
  189. Function maze_WestOpen(m.maze,x,y)
  190.     Return maze_diropen(m,x,y,3)
  191. End Function
  192.  
  193. ; display the maze on screen
  194. Function maze_display(m.maze,route)
  195.  
  196.     l.maze_loc = New maze_loc
  197.     For x = 0 To m\width - 1
  198.         For y = 0 To m\height - 1
  199.             l\x = x
  200.             l\y = y
  201.             maze_drawcell(m,l)
  202.             If route Then
  203.                 If (maze_getcell(m,x,y) And 240) > 0 Then
  204.                     maze_drawincell(x,y,0,255,0)
  205.                 End If
  206.             End If
  207.         Next
  208.     Next
  209.     Delete l
  210.  
  211. End Function
  212.  
  213. ; draw a single maze cell
  214. Function maze_drawcell(m.maze,l.maze_loc)
  215.     
  216.     mc.maze_control = First maze_control 
  217.     If mc <> Null Then
  218.     
  219.         x=l\x*mc\imgwidth
  220.         y=l\y*mc\imgheight
  221.     
  222.         p = maze_getcell(m,l\x,l\y) Mod 16
  223.     
  224.         DrawImage mc\img,x,y,p
  225.     End If
  226.     
  227. End Function
  228.  
  229. Function maze_drawIncell(px,py,r,g,b)
  230.     
  231.     mc.maze_control = First maze_control 
  232.     If mc <> Null Then
  233.     
  234.         x=px*mc\imgwidth
  235.         y=py*mc\imgheight
  236.         
  237.         Color r,g,b
  238.         Rect x+2,y+2,mc\imgwidth-4,mc\imgheight-4,1
  239.     End If
  240.     
  241. End Function
  242.  
  243.  
  244.  
  245. ; create a new maze, the setting the seed rather than using millisecs()
  246. ; will allow the same maze to be generated a number of times
  247. ; crossovers indicates how many extra links to put into the maze, this
  248. ; creates loops in the maze.
  249. Function maze_Create.maze(width,height,seed,crossovers)
  250.     m.maze = New maze
  251.     
  252.     m\width = width
  253.     m\height = height
  254.     m\crossing = (crossovers > 0)
  255.     
  256.     m\buffer = CreateBank(width * height)    
  257.     mc.maze_control = First maze_control
  258.     If mc <> Null Then
  259.         mc\count = mc\count + 1
  260.     End If
  261.     
  262.     maze_generate(m,crossovers,seed)
  263.     
  264.     Return m
  265. End Function
  266.  
  267. ; delete an individual maze
  268. Function maze_Delete(m.maze)
  269.  
  270.     mc.maze_control = First maze_control
  271.     If mc <> Null Then
  272.         mc\count = mc\count + 1
  273.     End If
  274.  
  275.     FreeBank m\buffer
  276.     Delete m  
  277. End Function
  278.  
  279. ; initialisation routine
  280. ; call at the start of the program, but after the graphics command
  281. Function maze_startup(filename$,imgwidth,imgheight)
  282.     mc.maze_control = New maze_control
  283.     mc\imgwidth = imgwidth
  284.     mc\imgheight = imgheight
  285.     mc\imgname$ = filename$
  286. ;    mc\img = LoadAnimImage(filename$,32,32,0,16)
  287.     mc\img = LoadAnimImage("mazeblocks.png",32,32,0,16)
  288.     ResizeImage mc\img,imgwidth,imgheight
  289.  
  290. End Function
  291.  
  292. ; cleanup routine, call at the end of the program to clear
  293. ; up any mazes left hanging round
  294. Function maze_shutdown()
  295.     For m.maze = Each maze
  296.         maze_delete(m)
  297.     Next
  298.     
  299.     mc.maze_control = First maze_control
  300.     If mc <> Null Then
  301.         FreeImage mc\img
  302.         Delete mc
  303.     End If
  304.     
  305. End Function
  306.  
  307.  
  308. ;Function maze_drawcell2(l.maze_loc)
  309. ;    x=l\x*imgsize
  310. ;    y=l\y*imgsize
  311. ;    os = imgsize / 4
  312. ;    
  313. ;    p = maze(l\x,l\y) Mod 16
  314. ;    
  315. ;    DrawImage img,x,y,p
  316. ;    Rect x+os,y+os,os,os,1
  317. ;    
  318. ;    
  319. ;End Function
  320.  
  321. ; calculate offset direction
  322. Function maze_dircalc(l.maze_loc,dir,r.maze_loc)
  323.     Select dir
  324.         Case 0
  325.             dx = 0
  326.             dy = -1
  327.         Case 1
  328.             dx = 1
  329.             dy = 0
  330.         Case 2
  331.             dx = 0
  332.             dy = 1
  333.         Case 3
  334.             dx = -1
  335.             dy = 0
  336.     End Select
  337.     
  338.     r\x = l\x+dx
  339.     r\y = l\y+dy
  340.     
  341. End Function    
  342.  
  343. ;Invert offset direction
  344. Function maze_Invdir(dir)
  345.     Return (dir + 2) Mod 4
  346. End Function
  347.  
  348.  
  349. ; flag xxxxxxx1 = new dir must be empty cell
  350. ;      xxxxxx1x = new dir must be 'used' cell
  351. ;      xxxxx1xx = new dir must not have an opening already
  352. Function maze_validdirs(m.maze,l.maze_loc,flags)
  353.  
  354. ;    DrawImage img,x,y,p
  355.  
  356.     c = 0
  357. ;    mc = First(maze_control)
  358. ;    If mc <> Null Then
  359.  
  360.         r.maze_loc = New maze_loc
  361.         For i = 0 To 3
  362.             maze_dircalc(l,i,r)
  363.             
  364.             If (r\x >= 0) And (r\x < m\width) And (r\y >= 0) And (r\y < m\height) Then
  365.                 If (flags And 1) Then
  366.                     If maze_getcell(m,r\x,r\y) > 0 Then c=c+1
  367.                 End If
  368.                 
  369.                 If (flags And 2) Then 
  370.                     If maze_getcell(m,r\x,r\y) = 0 Then c=c+1                
  371.                 End If
  372.                 
  373.                 If (flags And 4) Then
  374.                     If (maze_getcell(m,l\x,l\y) And (2^i)) =0 Then c = c + 1
  375.                 End If
  376.             End If
  377.     
  378.         Next 
  379.         Delete r
  380.         
  381. ;    End If
  382.  
  383.     
  384.     Return c
  385.  
  386. End Function
  387.  
  388. ; flag xxxxxxx1 = new dir must be empty cell
  389. ;      xxxxxx1x = new dir must be 'used' cell
  390. ;      xxxxx1xx = new dir must not have an opening already
  391. Function maze_dir(m.maze,l.maze_loc,flags,r.maze_loc)
  392.  
  393.  
  394.     safe = 0
  395.     result = -1
  396.     
  397.     If maze_validdirs(m,l,flags) = 0 Then result = 0
  398.     
  399.     While result = -1
  400.     
  401. ;        d=(Rnd(1)*4)-.5
  402.         d=Rand(0,3)
  403.         safe = safe + 1
  404.         If safe > 100 Then Stop
  405.             
  406.         maze_dircalc(l,d,r)    
  407.         
  408.         If (r\x >= 0) And (r\x < m\width) And (r\y >= 0) And (r\y < m\height) Then
  409.             If (flags And 1) Then
  410.                 If maze_getcell(m,r\x,r\y) > 0 Then result = 1
  411.             End If
  412.             
  413.             If (flags And 2) Then 
  414.                 If maze_getcell(m,r\x,r\y) = 0 Then result = 1        
  415.             End If
  416.  
  417.             If (flags And 4) Then
  418.                 If (maze_getcell(m,l\x,l\y) And (2^d)) =0 Then result = 1
  419.             End If
  420.             
  421.         End If
  422.         
  423.     Wend
  424.             
  425.     If result = 0 Then            
  426.         Return -1
  427.     Else
  428.         Return d
  429.     End If
  430.  
  431. End Function
  432.  
  433. Function maze_generate(m.maze,crossovers,seed)
  434.     SeedRnd seed
  435.     finished = 0
  436.     
  437.     For x = 0 To m\width-1
  438.         For y = 0 To m\height -1
  439.             maze_setcell(m,x,y,0) 
  440.         Next
  441.     Next 
  442.     
  443.     l.maze_loc = New maze_loc
  444.     r.maze_loc = New maze_loc
  445.     b.maze_loc = New maze_loc
  446.     b\x = 0
  447.     b\y = 0
  448.     l\x=b\x
  449.     l\y=b\y
  450.  
  451.     maze_setcell(m,l\x,l\y,16) 
  452.     While Not finished
  453.         ; move to new location
  454.         If maze_dir(m,l,2,r) > -1 Then
  455.             l\x = r\x
  456.             l\y = r\y
  457.         Else
  458.             newstart = 0
  459.             While Not newstart
  460.                 b\x = b\x + 1
  461.                 
  462.                 If b\x = m\width Then
  463.                     b\x = 0
  464.                     b\y = b\y + 1
  465.                     If b\y = m\height Then
  466.                         finished = 1
  467.                         newstart = 1
  468.                     End If
  469.                 End If
  470.                 
  471.                 If Not finished Then
  472.                     If maze_getcell(m,b\x,b\y) = 0 Then 
  473.                         newstart = 1
  474.                     End If
  475.                 End If
  476.             Wend
  477.     
  478.             l\x = b\x
  479.             l\y = b\y
  480.         End If
  481.         
  482.         If Not finished Then
  483.             d = maze_dir(m,l,1,r)
  484.             If d > -1 Then
  485.                 maze_setcell(m,l\x,l\y,maze_getcell(m,l\x,l\y) Or (2^d))
  486.                 maze_setcell(m,r\x,r\y,maze_getcell(m,r\x,r\y) Or (2^((d + 2) Mod 4)))    
  487. ;                maze_drawcell(m,r)
  488.             End If
  489.         End If
  490.     
  491.     Wend
  492.         
  493.     maze_setcell(m,0,0,maze_getcell(m,0,0) - 16)
  494.     
  495.     
  496.     ; generate crossovers
  497.     i = 0
  498.     While i < crossovers
  499.         fails = 0
  500.         Repeat
  501.             l\x = Rnd(1,m\width-2)
  502.             l\y = Rnd(1,m\height-2)
  503.             d = maze_dir(m,l,4,r)
  504.             If d > -1 Then
  505.                 m0 = maze_getcell(m,l\x,l\y)
  506.                 m1 = maze_getcell(m,r\x,r\y)
  507.  
  508.                 If (((m0 And 1)=1)+((m0 And 2)=2)+((m0 And 4)=4)+((m0 And 8)=8) > 1) Or (((m1 And 1)=1)+((m1 And 2)=2)+((m1 And 4)=4)+((m1 And 8)=8) > 1) Then
  509.                     d = -1
  510.                 Else
  511.                     If d = 0 Or d=2 Then
  512.                         If (((m0 And 2) = 2) And ((m1 And 2) = 2)) Or (((m0 And 8) = 8) And ((m1 And 8) = 8)) Then
  513.                             d = -1
  514.                         End If
  515.                     Else
  516.                         If (((m0 And 1) = 1) And ((m1 And 1) = 1)) Or (((m0 And 4) = 4) And ((m1 And 4) = 4)) Then
  517.                             d = -1
  518.                         End If                
  519.                     End If
  520.                 End If
  521.             End If
  522.             If d = -1 Then fails = fails + 1
  523.         Until d > -1 Or fails > 50
  524.         
  525.         If d > -1 Then
  526.                 maze_setcell(m,l\x,l\y,maze_getcell(m,l\x,l\y) Or (2^d))
  527.                 maze_setcell(m,r\x,r\y,maze_getcell(m,r\x,r\y) Or (2^((d + 2) Mod 4)))    
  528.         End If
  529.     
  530.         i = i + 1
  531.     Wend
  532.     
  533.     
  534.     Delete l
  535.     Delete r
  536.     Delete b
  537.  
  538. End Function
  539.  
  540. Type maze_node
  541.     Field x,y
  542.     Field dir,tries
  543.     Field edir
  544. End Type
  545.  
  546. Function maze_clearRoute(m.maze)
  547.     ; clear out route information
  548.     For x = 0 To m\width-1
  549.         For y = 0 To m\height-1
  550.             maze_setcell(m,x,y,(maze_getcell(m,x,y) And 15))
  551.         Next
  552.     Next
  553. End Function
  554.  
  555. Function maze_allSolve(m.maze,fx,fy)
  556.     maze_clearroute(m.maze)
  557.     For x = 0 To m\width-1
  558.         For y = 0 To m\height-1
  559.             If (x <> fx Or y <> fy) And ((maze_getcell(m,x,y) And 240) = 0) Then
  560.                 maze_solve(m,x,y,fx,fy)
  561.             End If
  562.         Next
  563.     Next
  564. End Function
  565.  
  566. ; solve the maze using a fairly simple "wall hugger" style process
  567. Function maze_solve(m.maze,sx,sy,fx,fy)
  568.  If Not m\crossing Then
  569.     ; create a workspace same size as the map.
  570.     count = 1
  571.     
  572.     n.maze_node = New maze_node
  573.     n\x = sx
  574.     n\y = sy
  575.     n\dir = -1
  576.     n\tries = 0
  577.     n\edir = 16
  578.     finished = 0
  579.     
  580.     
  581.     l.maze_loc = New maze_loc
  582.     r.maze_loc = New maze_loc
  583.     
  584.     While (n\x <> fx Or n\y <> fy) And (finished = 0)
  585.  
  586.     
  587.         n\tries = n\tries + 1
  588.         n\dir = (n\dir + 1) Mod 4
  589.         If n\tries > 4 Then
  590.             Delete n
  591.             n = Last maze_node
  592.         Else
  593.             If maze_diropen(m,n\x,n\y,n\dir) And n\dir <> n\edir Then
  594.                 count = count + 1
  595.                 ; that dir is open    
  596.                 l\x = n\x
  597.                 l\y = n\y
  598.                 dir = n\dir
  599.                 maze_dircalc(l,n\dir,r)
  600.                 n = New maze_node
  601.                 n\x = r\x
  602.                 n\y = r\y
  603.                 n\dir = dir
  604.                 n\tries = 0
  605.                 n\edir = maze_invdir(dir)
  606.             End If
  607.         End If
  608.     Wend
  609.     
  610.     Delete l
  611.     Delete r
  612.     
  613.     
  614.     ;create final codestring & inscribe on maze
  615.     cs$ = ""
  616.     For n = Each maze_node
  617.         maze_setcell(m,n\x,n\y,(maze_getcell(m,n\x,n\y) And 15) Or (2^(n\dir+4)))
  618.         cs$ = cs$ + Str$(n\dir)
  619.     Next
  620.  
  621.     ; clear the workspace at the end
  622.     Delete Each maze_node
  623.   End If
  624.   Return cs$
  625. End Function
  626.  
  627. Function Maze_Save(m.maze,filename$)
  628.     ; Open a file to write to
  629.     fileout = WriteFile(filename$)
  630.     WriteInt fileout,m\width
  631.     WriteInt fileout,m\height
  632.     WriteInt fileout,m\crossing
  633.     WriteBytes m\buffer,fileout,0,m\width*m\height
  634.     CloseFile fileout
  635. End Function
  636.  
  637. Function Maze_Load.maze(filename$)
  638.     m.maze = New maze
  639.     filein = ReadFile(filename$)
  640.     m\width = ReadInt( filein)
  641.     m\height = ReadInt( filein)
  642.     m\crossing = ReadInt(filein)
  643.     m\buffer = CreateBank(m\width*m\height)
  644.     ReadBytes m\buffer,filein,0,m\width*m\height
  645.     
  646.     Return m
  647.  
  648. End Function